/*************************************************************************************
 * Nombre del archivo: Generador_Codigo_Parser.java
 * Universidad del Valle de Guatemala
 * Curso: Diseño de Lenguajes de Programación
 * @author martin
 *          Martín Guzmán 08041
 * Fecha: mayo de 2010
 * Descripción: Generador de analizador sintáctico
*************************************************************************************/
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

class Generador_Codigo_Parser {

    HashMap<String, String> Productions;
    LinkedList<String> tokens_parser = new LinkedList<String>();
    LinkedList<String> metodos_parser = new LinkedList<String>();
    LinkedList<String> lista_errores = new LinkedList<String>();
    int tabulado =0;

    public Generador_Codigo_Parser(HashMap<String, String> Productions) {
        this.Productions = Productions;
        this.tokens_parser = DefCocol.tokens_en_parser;
        this.Generar_Metodos();
        this.Generar_Clase_Parser();
    }

    /***********************************************
     * Método: Generar_Metodos
     * Uso: Manda a llamar para generar todos los métodos
    ***********************************************/
    private void Generar_Metodos() {
        this.Generar_Metodos_Generales();
        this.Generar_Constructor();
        this.Generar_Metodos_Productions();
        this.Generar_Metodo_Errores();
    }

    /***********************************************
     * Método: Generar_Clase_Parser
     * Uso: Genera toda la clase y la manda a guardar
    ***********************************************/
    private void Generar_Clase_Parser() {
        String parser_total = (
            "/*************************************************************************************"+"\n"+
            " * Nombre del archivo: "+Main.nombre_parser+".java"+"\n"+
            " * Universidad del Valle de Guatemala"+"\n"+
            " * Curso: Diseño de Lenguajes de Programación"+"\n"+
            " * @author martin"+"\n"+
            " *          Martín Guzmán 08041"+"\n"+
            " * Fecha: Mayo de 2010"+"\n"+
            " * Descripción: tercer proyecto. GENERADOR DE ANALIZADOR SINTÁCTICO. Clase que contiene el Parser" +"\n"+
            " *          generado para el archivo de entrada utilizando el Scanner (también generado)"+"\n"+
            "*************************************************************************************/"+"\n"+
            ""+"\n"+
            "import java.util.LinkedList;"+"\n"+
            ""+"\n"+
            "public class "+Main.nombre_parser+" {"+"\n"+
            ""+"\n"+
            "\tfinal int maxToken = 14;"+"\n"+
            "\tpublic Token LastToken;"+"\n"+
            "\tpublic Token LAToken;"+"\n"+
            "\tpublic int MaxErrors = 5;"+"\n"+
            "\tpublic int Errores_encontrados=0;"+"\n"+
            "\tpublic GeneratedScanner Scanner;"+"\n"+
            "\t;"+"\n"+
            ""+"\n"
        );
        
        for(int i=0;i<this.metodos_parser.size();i++){
            parser_total+="\n"+
                    this.metodos_parser.get(i)
                    +"\n";
        }

        parser_total+="\n"+"}";

        //if(this.error==false){
            this.Guardar_Archivo(Main.path_to_save, Main.nombre_parser+".java", parser_total);
        //}
    }

    /***********************************************
     * Método: Guardar_Archivo
     * Uso: guarda texto en un archivo en path/nombre
     * @param nombre contiene el nombre y la extensión del archivo
     * @param pah contiene únicamente el path del archivo
     * @param texto contiene lo que se desea guardar
    ***********************************************/
    public void Guardar_Archivo(String path, String nombre, String texto){
        //System.out.println("Guardar_Archivo: texto a guardar: "+texto);
        String archivo = path+"/"+nombre;
        //boolean booleano = new File(archivo).mkdir();

        File selectedFile = new File(archivo);
        try {
            PrintWriter fileOut = new PrintWriter(new FileWriter(selectedFile));
            fileOut.print(texto);
            fileOut.close();
        }catch (Exception ex){
            Main.Print("No se pudo guardar el archivo: "+archivo);
        }
    }

    /***********************************************
     * Método: Tab
     * Uso: Devuelve el tabulado correcto
     * @return String con el tabulado
    ***********************************************/
    public String Tab() {
        String retorno = "";
        for(int i=0;i<this.tabulado;i++){
            retorno += "\t";
        }
        return retorno;
    }

    /***********************************************
     * Método: Generar_Metodos_Generales
     * Uso: Genera los métodos de uso general..
     *      como expect, complementar_cadena...
    ***********************************************/
    public void Generar_Metodos_Generales() {
        String Expect = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: Expect"+"\n"+
            "\t"+"* Uso: Si el LAToken no contiene el valor pedido, es un error"+"\n"+
            "\t"+"* @param n, que es el número de token a esperar"+"\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public void Expect(int n){"+"\n"+
            "\t"+"	if(LAToken.getKind() == n){"+"\n"+
            "\t"+"		Get();"+"\n"+
            "\t"+"	}"+"\n"+
            "\t"+"	else{"+"\n"+
            "\t"+"		SynError(n);"+"\n"+
            "\t"+"	}"+"\n"+
            "\t"+"}"+"\n"+
         "");

        String Get = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: Get"+"\n"+
            "\t"+"* Uso: Obtiene el siguiente token del Scanner"+"\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public void Get(){"+"\n"+
            "\t"+"	LastToken = LAToken.clone();"+"\n"+
            "\t"+"	LAToken = Scanner.Scan();"+"\n"+
            "\t"+"}"+"\n"+
         "");

        //agrega Expect, Get, SynError a la lista de métodos
        this.metodos_parser.add(Expect);
        this.metodos_parser.add(Get);
        
    }

    /***********************************************
     * Método: Generar_Constructor
     * Uso: Genera el método constructor
    ***********************************************/
    public void Generar_Constructor() {

        String Constructor = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: Parser"+"\n"+
            "\t"+"* Uso: Constructor"+"\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public void Parser(){}"+"\n"+
         "");
        Iterator iterator = this.Productions.keySet().iterator();
        String S = (String)iterator.next() +"();"; //no terminal inicial
        String Parse = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: Parse"+"\n"+
            "\t"+"* Uso: Empieza a parsear"+"\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public void Parse(GeneratedScanner scan){"+"\n"+
            "\t"+"	this.Scanner = scan;"+"\n"+
            "\t"+"	this.LAToken = new Token();"+"\n"+
            "\t"+"	Get();"+"\n"+
            "\t"+"	"+S+"\n"+
            "\t"+"	Expect(-1);"+"\n"+
            "\t"+"}"+"\n"+
         "");
        this.metodos_parser.add(Parse);
        this.metodos_parser.addFirst(Constructor);
    }

    /***********************************************
     * Método: Generar_Metodos_Productions
     * Uso: Para generar todos los métodos de las producciones
    ***********************************************/
    public void Generar_Metodos_Productions() {

        Iterator iterator = this.Productions.keySet().iterator();
        while(iterator.hasNext()){
            String cadena = (String)iterator.next();
            String metodo = this.Metodo_Production(cadena,this.Productions.get(cadena));
            this.metodos_parser.add(metodo);
        }
    }

    /***********************************************
     * Método: Metodo_Production
     * Uso: Genera el código para 1 produccion
    ***********************************************/
    public String Metodo_Production(String nombre_con_param_y_semAction, String derecha) {

        Retorno_Funcion ret = DefCocol.expect(DefCocol.ident, nombre_con_param_y_semAction);
        //---------------------------------
        String nombre_metodo = ret.cadena;
        //---------------------------------
        String param_semAction = DefCocol.Complementar_cadena(nombre_con_param_y_semAction, ret.indice);
        param_semAction = Borrar_espacios_al_inicio(param_semAction);
        ret = DefCocol.Attributes(param_semAction);
        //---------------------------------
        String parametros ="";
        //---------------------------------
        //---------------------------------
        String retorno = "";
        //---------------------------------
        //---------------------------------
        String tipo_retorno = "void";
        //---------------------------------
        //---------------------------------
        String declaracion = "";
        //---------------------------------
        if(ret.aceptado == true){
            //para quitarle los <.   .>
            String Atributos = ret.cadena.substring(2, ret.cadena.length()-2);
            String[] arreglo = this.obtener_parametros(Atributos);
            tipo_retorno = arreglo[0];
            retorno = arreglo[1];
            if(arreglo[2]==null){
                parametros = "";
            }
            else{
                parametros = arreglo[2];
            }
            declaracion = arreglo[3];
        }
        String semAction_ = DefCocol.Complementar_cadena(param_semAction, ret.indice);
        ret = DefCocol.SemAction(semAction_);
        String semAction = "";
        if(ret.aceptado == true){
            semAction = ret.cadena.substring(2, ret.cadena.length()-2);
        }
        
        this.tabulado = 1;
        String contenido = Codigo_Derecha_Produccion(derecha);
        //---------------------------------
        
        String metodo = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: "+nombre_metodo+"\n"+
            "\t"+"* Uso: Evalua la producción: "+ derecha + "\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public "+tipo_retorno+" "+nombre_metodo+"("+parametros+"){"+"\n"+
            "\t"+"	"+declaracion+"\n"+
            "\t"+"	"+semAction+"\n"+
            "\t"+""+contenido+"\n"+
            "\t"+"	"+retorno+"\n"+
            "\t"+"}"+"\n"+
        "");
        return metodo;
    }

     /***********************************************
     * Método: Borrar_espacios_al_inicio
     * Uso: Retorna la cadena con los espacios borrados del inicio
     * @return String con la cadena sin espacios al inicio
    ***********************************************/
    public String Borrar_espacios_al_inicio(String cadena){
        try{
            Retorno_Funcion ret = DefCocol.expect(DefCocol.space, cadena);
            if(ret.aceptado == true){
                return DefCocol.Complementar_cadena(cadena, ret.indice);
            }
            return cadena;
        }catch(Exception exp){
            return cadena;
        }
    }

    /***********************************************
     * Método: Codigo_Derecha_Produccion
     * Uso: Genera el código del lado derecho de una producción
     * @return Con el código para el lado derecho de la producción
    ***********************************************/
    public String Codigo_Derecha_Produccion(String derecha) {
        
        String codigo = "";
        //----------------------------------------------------------------------
        String cadena = this.Borrar_espacios_al_inicio(derecha);
        //----------------------------------------------------------------------
        LinkedList<String> Terms = new LinkedList<String>();

        Retorno_Funcion ret = DefCocol.Term(cadena);
        if(ret.aceptado){
            Terms.add(ret.cadena);
            cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
        }
        while(true){
            ret = DefCocol.expect(DefCocol.space+"(\\|)"+DefCocol.space, cadena);
            if(ret.aceptado == true){
                cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
                ret = DefCocol.Term(cadena);
                if(ret.aceptado){
                    Terms.add(ret.cadena);
                    cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
                }
            }
            else{
                break;
            }
        }

        if(Terms.size()==1){
            String cadena_term = Tab()+"//Term"+"\n";
            this.tabulado++;
            cadena_term+=Codigo_para_Term(Terms.get(0));
            this.tabulado--;
            codigo+=cadena_term;
        }
        else{
            this.tabulado++;
            codigo = Tab()+"switch(LAToken.getKind()){"+"\n";
            this.tabulado++;
            for(int i=0;i<Terms.size();i++){
                String cadena_term = Tab()+"//Term"+i+"\n";
                //CALCULAR PRIMERO Y PONER CON CADA ELEMENTO DE PRIMERO UN CASE ANTES DEL CODIGO
                LinkedList<Integer> PRIMERO = this.Primero(this.Borrar_espacios_al_inicio(Terms.get(i)));
                //System.out.println("Codigo_Derecha_Produccion: Primero = "+PRIMERO.toString());
                for(int j=0;j<PRIMERO.size();j++){
                    cadena_term+= Tab()+"case "+PRIMERO.get(j)+":"+"\n";
                }
                this.tabulado++;
                cadena_term+=Tab()+Codigo_para_Term(Terms.get(i));
                cadena_term+= Tab()+"break;"+"\n";
                this.tabulado--;
                
                codigo+=cadena_term;

            }
            this.tabulado--;
            codigo += Tab()+"}"+"\n";
            this.tabulado--;
        }
        return codigo;
    }

    /***********************************************
     * Método: Codigo_para_Term
     * Uso: Genera el código de 1 term
     * @return String con el cógigo de un term
    ***********************************************/
    public String Codigo_para_Term(String Term_cadena) {

        String codigo ="";

        String cadena = Term_cadena;

        LinkedList<String> Factors = new LinkedList<String>();

        while(true){
            //borra todos los espacios al inicio
            cadena = this.Borrar_espacios_al_inicio(cadena);
            Retorno_Funcion ret = DefCocol.Factor(cadena);
            if(ret.aceptado){
                //System.out.println("\tFactor: "+ret.cadena);
                Factors.add(ret.cadena);
                cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
            }
            else{
                break;
            }
        }
        //System.out.println("\tCantidad de factors: "+Factors.size());

        for(int i=0;i<Factors.size();i++){
            //this.tabulado++;
            String cadena_factor = Tab()+"// -> Factor:"+Factors.get(i).replaceAll("\\n", "")+"\n";
            cadena_factor+=Codigo_para_Factor(Factors.get(i));
            codigo+=cadena_factor;
            //this.tabulado--;
        }
        
        return codigo;
    }

    /***********************************************
     * Método: Codigo_para_Factor
     * Uso: Genera el código de 1 factor
     * @return String con el cógigo de un factor
    ***********************************************/
    public String Codigo_para_Factor(String Factor_cadena) {
        String cadena = this.Borrar_espacios_al_inicio(Factor_cadena);
        String codigo = "";

        if(DefCocol.Symbol(cadena).aceptado == true){
            codigo = Codigo_para_Symbol(cadena);
        }
        else if(DefCocol.SemAction(cadena).aceptado == true){
            String car = DefCocol.SemAction(cadena).cadena;
            codigo = Tab()+car.substring(2, car.length()-2)+"\n";
        }
        else if(cadena.startsWith("{")){
            codigo = Tab()+"//{"+"\n";
            //******************************************************************
            //USAR PRIMERO PARA CALCULAR LA CONDICIÓN DEL WHILE
            LinkedList<Integer> PRIMERO = Primero(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            String condiciones = "LAToken.getKind()=="+PRIMERO.get(0);
            for(int i = 1;i<PRIMERO.size(); i++){
                condiciones+=" || "+"LAToken.getKind()=="+PRIMERO.get(i);
            }
            //******************************************************************
            codigo += Tab()+"while("+condiciones+"){"+"\n";
            codigo += this.Codigo_Derecha_Produccion(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            codigo += Tab()+"}"+"\n";
            codigo += Tab()+"//}"+"\n";
        }
        else if(cadena.startsWith("[")){
            codigo = Tab()+"//["+"\n";
            //******************************************************************
            //USAR PRIMERO PARA CALCULAR LA CONDICIÓN DEL WHILE
            LinkedList<Integer> PRIMERO = Primero(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            String condiciones = "LAToken.getKind()=="+PRIMERO.get(0);
            for(int i = 1;i<PRIMERO.size(); i++){
                condiciones+=" || "+"LAToken.getKind()=="+PRIMERO.get(i);
            }
            //******************************************************************
            codigo += Tab()+"if("+condiciones+"){"+"\n";
            codigo += this.Codigo_Derecha_Produccion(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            codigo += Tab()+"}"+"\n";
            codigo += Tab()+"//]"+"\n";
        }
        else if(cadena.startsWith("(")){
            codigo = Tab()+"//("+"\n";
            
            int numero_error = this.lista_errores.size()+Generador_Analizadores.Keywords_KeywordDecl.size()+Generador_Analizadores.Tokens_TokenDecl.size();
            String nombre_de_metodo="";

            LinkedList<String> nombres_metodos = new LinkedList<String>();
            Iterator iterator = this.Productions.keySet().iterator();
            while(iterator.hasNext()){
                String nombre = (String) iterator.next();
                if(nombre.contains("<.")){
                    nombres_metodos.add(nombre);
                }
                else{
                    nombres_metodos.add(nombre);
                }
            }
            nombre_de_metodo = nombres_metodos.get(this.lista_errores.size());

            this.lista_errores.add("-> "+ "production invalid");

            //SynError
            //******************************************************************
            //USAR PRIMERO PARA CALCULAR LA CONDICIÓN DEL WHILE
            LinkedList<Integer> PRIMERO = Primero(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            String condiciones = "LAToken.getKind()=="+PRIMERO.get(0);
            for(int i = 1;i<PRIMERO.size(); i++){
                condiciones+=" || "+"LAToken.getKind()=="+PRIMERO.get(i);
            }
            //******************************************************************
            codigo += Tab()+"if("+condiciones+"){"+"\n";
            codigo += this.Codigo_Derecha_Produccion(this.Borrar_espacios_al_inicio(Factor_cadena.substring(1,Factor_cadena.length()-1)));
            codigo += Tab()+"}"+"\n";
            codigo += Tab()+"else {"+"\n";
            codigo += Tab()+"\tSynError("+numero_error+");"+"\n";
            codigo += Tab()+"}"+"\n";
            codigo += Tab()+"//)"+"\n";
        }
        
        return codigo;
    }

    /***********************************************
     * Método: Codigo_para_Symbol
     * Uso: Genera el código de 1 Symbol
     * @return String con el cógigo de un Symbol
    ***********************************************/
    public String Codigo_para_Symbol(String Symbol) {

        String cadena = this.Borrar_espacios_al_inicio(Symbol);
        String codigo = "";
        Retorno_Funcion ret = DefCocol.expect(DefCocol.ident, cadena);
        Retorno_Funcion ret2 = DefCocol.expect(DefCocol.string, cadena);

        //revisa si es un ident
        //System.out.println("DefCocol.tokens: "+DefCocol.tokens.toString());
        //System.out.println("DefCocol.Productions: "+Productions.toString());
        //System.out.println("Symbol: ."+Symbol+".");
        if(ret.aceptado == true){
            //System.out.println("Es un ident");
            //puede ser de 3 cosas.. un token, un token del parser o una producción..
            if(DefCocol.tokens.contains(ret.cadena)){
                int numero = DefCocol.tokens.indexOf(ret.cadena);
                codigo = Tab()+"Expect("+numero+");"+"\n";
            }
            else{
                String cad = Symbol;
                Retorno_Funcion ret11 = DefCocol.expect(DefCocol.ident, cad);
                String ident = ret11.cadena;
                cad = DefCocol.Complementar_cadena(cad, ret11.indice);
                cad = this.Borrar_espacios_al_inicio(cad);
                ret11 = DefCocol.Attributes(cad);
                String param = "";
                String almacenar_en = "";
                if(ret11.aceptado == true){
                    String param_incompleto = ret11.cadena.substring(2, ret11.cadena.length()-2);
                    LinkedList<String> lista = this.obtener_almacenar_en(param_incompleto);
                    param = lista.get(0);
                    almacenar_en = lista.get(1);
                }
                codigo = Tab()+almacenar_en+ident+"("+param+");"+"\n";
            }
        }
        else if(ret2.aceptado == true){
            //System.out.println("Es un string");
            //es un string de los tokens..
            if(DefCocol.tokens.contains(ret2.cadena)){
                int numero = DefCocol.tokens.indexOf(ret2.cadena);
                codigo = Tab()+"Expect("+numero+");"+"\n";
            }
        }
        return codigo;
    }

    /***********************************************
     * Método: Agregar_lista
     * Uso: Agrega los elementos a la lista
     * @return por referencia
    ***********************************************/
    public void Agregar_lista(LinkedList<Integer> fuente, LinkedHashSet<Integer> destino){
        for(int i = 0;i<fuente.size();i++){
            destino.add(fuente.get(i));
        }
    }

    /***********************************************
     * Método: Primero
     * Uso: Hace el cálculo de primero de lo que se mande
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public LinkedList<Integer> Primero(String cadena_inicial) {

        LinkedHashSet<Integer> retorno = new LinkedHashSet<Integer>();

        //----------------------------------------------
        this.Agregar_lista(this.Primero_Production(cadena_inicial), retorno);
        //----------------------------------------------
        
        LinkedList<Integer> retorno_final = new LinkedList<Integer>();
        Object[] lista = retorno.toArray();
        for(int i=0;i<lista.length;i++){
            retorno_final.add((Integer)lista[i]);
        }

        return retorno_final;
    }

    /***********************************************
     * Método: Primero_Term
     * Uso: hace la función primero de un Term
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public LinkedList<Integer> Primero_Production(String cadena_inicial) {

        LinkedHashSet<Integer> retorno = new LinkedHashSet<Integer>();

        String codigo = "";
        //----------------------------------------------------------------------
        String cadena = this.Borrar_espacios_al_inicio(cadena_inicial);
        //----------------------------------------------------------------------
        LinkedList<String> Terms = new LinkedList<String>();

        Retorno_Funcion ret = DefCocol.Term(cadena);
        if(ret.aceptado){
            Terms.add(ret.cadena);
            cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
        }
        while(true){
            ret = DefCocol.expect(DefCocol.space+"(\\|)"+DefCocol.space, cadena);
            if(ret.aceptado == true){
                cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
                ret = DefCocol.Term(cadena);
                if(ret.aceptado){
                    Terms.add(ret.cadena);
                    cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
                }
            }
            else{
                break;
            }
        }

        for(int i = 0;i<Terms.size();i++){
            this.Agregar_lista(this.Primero_Term(Terms.get(i)), retorno);
        }

        LinkedList<Integer> retorno_final = new LinkedList<Integer>();
        Object[] lista = retorno.toArray();
        for(int i=0;i<lista.length;i++){
            retorno_final.add((Integer)lista[i]);
        }

        return retorno_final;
    }

    /***********************************************
     * Método: Primero_Term
     * Uso: hace la función primero de un Term
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public LinkedList<Integer> Primero_Term(String cadena_inicial) {

        LinkedHashSet<Integer> retorno = new LinkedHashSet<Integer>();

        String codigo ="";
        String cadena = cadena_inicial;
        LinkedList<String> Factors = new LinkedList<String>();

        while(true){
            //borra todos los espacios al inicio
            cadena = this.Borrar_espacios_al_inicio(cadena);
            Retorno_Funcion ret = DefCocol.Factor(cadena);
            if(ret.aceptado){
                //System.out.println("\tFactor: "+ret.cadena);
                Factors.add(ret.cadena);
                cadena = DefCocol.Complementar_cadena(cadena, ret.indice);
            }
            else{
                break;
            }
        }
        //agrega PRIMERO de la lista de factores...
        this.Agregar_lista(this.Primero_Factor(Factors), retorno);

        LinkedList<Integer> retorno_final = new LinkedList<Integer>();
        Object[] lista = retorno.toArray();
        for(int i=0;i<lista.length;i++){
            retorno_final.add((Integer)lista[i]);
        }

        return retorno_final;
    }

    /***********************************************
     * Método: Primero_Factor
     * Uso: hace la función primero de un Factor
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public LinkedList<Integer> Primero_Factor(LinkedList<String> Factors) {

        LinkedHashSet<Integer> retorno = new LinkedHashSet<Integer>();

        for(int i = 0;i<Factors.size();i++){

            String cadena = this.Borrar_espacios_al_inicio(Factors.get(i));
            String codigo = "";

            if(DefCocol.Symbol(cadena).aceptado == true){
                this.Agregar_lista(this.Primero_Symbol(cadena), retorno);
                break;
            }
            else if(DefCocol.SemAction(cadena).aceptado == true){
                //deja pasar.. porque no tiene PRIMERO (deja que calcule con el siguiente factor)
            }
            else if(cadena.startsWith("{")){
                //quita los {}
                String cadena1 = cadena.substring(1, cadena.length()-1);
                this.Agregar_lista(this.Primero_Production(cadena1), retorno);
                //deja pasar.. porque no tiene PRIMERO (deja que calcule con el siguiente factor)
            }
            else if(cadena.startsWith("[")){
                //quita los []
                String cadena1 = cadena.substring(1, cadena.length()-1);
                this.Agregar_lista(this.Primero_Production(cadena1), retorno);
                //deja pasar.. porque no tiene PRIMERO (deja que calcule con el siguiente factor)
            }
            else if(cadena.startsWith("(")){
                //quita los ()
                String cadena1 = cadena.substring(1, cadena.length()-1);
                this.Agregar_lista(this.Primero_Production(cadena1), retorno);
                if(retorno.isEmpty() == false){
                    break;
                }
            }
        }

        LinkedList<Integer> retorno_final = new LinkedList<Integer>();
        Object[] lista = retorno.toArray();
        for(int i=0;i<lista.length;i++){
            retorno_final.add((Integer)lista[i]);
        }

        return retorno_final;
    }

    /***********************************************
     * Método: Primero_Symbol
     * Uso: hace la función primero de un Symbol
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public LinkedList<Integer> Primero_Symbol(String Symbol) {

        LinkedHashSet<Integer> retorno = new LinkedHashSet<Integer>();

        String cadena = this.Borrar_espacios_al_inicio(Symbol);
        String codigo = "";
        Retorno_Funcion ret = DefCocol.expect(DefCocol.ident, cadena);
        Retorno_Funcion ret2 = DefCocol.expect(DefCocol.string, cadena);

        //revisa si es un ident
        if(ret.aceptado == true){
            //System.out.println("Es un ident");
            //puede ser de 3 cosas.. un token, un token del parser o una producción..
            if(DefCocol.tokens.contains(ret.cadena)){
                int numero = DefCocol.tokens.indexOf(ret.cadena);
                retorno.add(numero);
            }
            else{
                //SIGNIFICA QUE ES OTRA PRODUCCIÓN
                String cad = Symbol;
                String production = this.Obtener_Contenido_Production(ret.cadena);
                //manda a llamar a primero de la otra producción...
                this.Agregar_lista(this.Primero_Production(production), retorno);
            }
        }
        else if(ret2.aceptado == true){
            //System.out.println("Es un string");
            //es un string de los tokens..
            if(DefCocol.tokens.contains(ret2.cadena)){
                int numero = DefCocol.tokens.indexOf(ret2.cadena);
                retorno.add(numero);
            }
        }

        LinkedList<Integer> retorno_final = new LinkedList<Integer>();
        Object[] lista = retorno.toArray();
        for(int i=0;i<lista.length;i++){
            retorno_final.add((Integer)lista[i]);
        }

        return retorno_final;
    }

    /***********************************************
     * Método: Obtener_Contenido_Production
     * Uso: Devuelve el contenido de una producción
     * @return Lista con los elementos en PRIMERO(cadena)
    ***********************************************/
    public String Obtener_Contenido_Production(String ident) {

        Iterator iterator = this.Productions.keySet().iterator();
        while(iterator.hasNext()){
            String nombre = (String) iterator.next();
            if(nombre.startsWith(ident)){
                return this.Productions.get(nombre);
            }
        }
        
        return "";
    }

    /***********************************************
     * Método: obtener_parametros
     * Uso:
     * @return
    ***********************************************/
    public String[] obtener_parametros(String Atributos) {
        String[] retorno = new String[4];

        for(String a : retorno){
            a = "";
        }

        String[] arreglo = Atributos.split(",");

        if(arreglo[0].startsWith("out")){
            String[] out = arreglo[0].split(" ");

            LinkedList<String> out_mod = new LinkedList<String>();
            for(String a : out){
                if(a.isEmpty()==false)
                    out_mod.add(a);
            }

            retorno[0] = out_mod.get(1)+"";
            if(out_mod.get(2).endsWith(";") == false){
                retorno[1] = "return "+out_mod.get(2)+";";
            }
            else{
                retorno[1] = "return "+out_mod.get(2)+"";
            }
            
            for(int i=1;i<arreglo.length;i++){
                if(arreglo[i]!=null){
                    retorno[2] += arreglo[i];
                }
            }

            if(retorno[0].compareToIgnoreCase("double")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("int")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("boolean")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = false;";
            }
            else if(retorno[0].compareToIgnoreCase("char")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("float")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("long")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("byte")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
            else if(retorno[0].compareToIgnoreCase("short")==0){
                retorno[3] = retorno[0] + " "+ out_mod.get(2)+" = 0;";
            }
        }
        else{
            retorno[0] = "void";
            retorno[1] = "";
            retorno[2] = Atributos;
            retorno[3] = "";
        }
        //tipo_retorno = arreglo[0];
        //retorno = arreglo[1];
        //parametros = arreglo[2];

        return retorno;
    }

    /***********************************************
     * Método: obtener_almacenar_en
     * Uso:
     * @return
    ***********************************************/
    public LinkedList<String> obtener_almacenar_en(String param_incompleto) {
        LinkedList<String> retorno = new LinkedList<String>();
        //param = lista.get(0);
        //almacenar_en = lista.get(1);
        LinkedList<String> cortada_en_parametros = new LinkedList<String>();
        for(String a : param_incompleto.split(",")){
            if(a.isEmpty()==false){
                cortada_en_parametros.add(a);
            }
        }

        if(cortada_en_parametros.getFirst().startsWith("out")){
            String param = "";
            for(int i = 1;i<cortada_en_parametros.size();i++){
                param+=cortada_en_parametros.get(i);
            }
            retorno.add(param);
            retorno.add(cortada_en_parametros.getFirst().split(" ")[1] + " = ");
        }
        else{
            retorno.add(param_incompleto);//parametros
            retorno.add("");//a =
        }

        return retorno;
    }

    private String Obtener_errores_en_switch() {
        String retorno = "";

        retorno+="\t\t\tcase "+(-1)+":"+"\n";
        retorno+="\t\t\t\tError = \""+"EOF Expected\";"+"\n";
        retorno+="\t\t\t\tbreak;"+"\n";

        int contador = 0;
        Iterator iterator = Generador_Analizadores.Keywords_KeywordDecl.keySet().iterator();
        while( iterator. hasNext() ){
            String nombre = (String) iterator.next();
            String nombre2 = nombre;
            if(nombre.endsWith("_0")){
                nombre= nombre.substring(0, nombre.length()-2);
            }
            if(nombre.contains("\"")){
                nombre = nombre.replaceAll("\\\"", "\\\"");
            }
            retorno+="\t\t\tcase "+(contador++)+":"+"\n";
            retorno+="\t\t\t\tError = \""+nombre+" Expected\";"+"\n";
            retorno+="\t\t\t\tbreak;"+"\n";
        }
        iterator = Generador_Analizadores.Tokens_TokenDecl.keySet().iterator();
        while( iterator. hasNext() ){
            String nombre = (String) iterator.next();
            if(nombre.endsWith("_0")){
                nombre= nombre.substring(0, nombre.length()-2);
            }
            
            retorno+="\t\t\tcase "+(contador++)+":"+"\n";
            if(nombre.startsWith("Token_Parser") == true){
                String cadena = Generador_Analizadores.Tokens_TokenDecl.get(nombre);
                if(cadena.contains("\"")){
                    cadena = cadena.replaceAll("\\\"", "\\\\\"");
                }
                retorno+="\t\t\t\tError = \""+cadena+" Expected\";"+"\n";
            }
            else{
                retorno+="\t\t\t\tError = \""+nombre+" Expected\";"+"\n";
            }
            retorno+="\t\t\t\tbreak;"+"\n";
        }

        for(int i = 0;i<this.lista_errores.size();i++){
            retorno+="\t\t\tcase "+(contador++)+":"+"\n";
            retorno+="\t\t\t\tError = \""+this.lista_errores.get(i)+"\";"+"\n";
            retorno+="\t\t\t\tbreak;"+"\n";
        }
        
        return retorno;
    }


    private void Generar_Metodo_Errores() {
        String obtener_switch_case_errors = this.Obtener_errores_en_switch();
         String SynError = (""+
            "\t"+"/***********************************************"+"\n"+
            "\t"+"* Método: SynError"+"\n"+
            "\t"+"* Uso: Imprime el número de error que se obtuvo"+"\n"+
            "\t"+"* @param n, que es el número de token a ver de error"+"\n"+
            "\t"+"***********************************************/"+"\n"+
            "\t"+"public void SynError(int n){"+"\n"+
            "\t"+"	Errores_encontrados++;"+"\n"+
            "\t"+"	String Error = \"\";"+"\n"+
            "\t"+"	switch(n){"+"\n"+
            "\t"+""+obtener_switch_case_errors+"\n"+
            "\t"+"	default:"+"\n"+
            "\t"+"		Error = \"Error desconocido: \"+n;"+"\n"+
            "\t"+"	}"+"\n"+
            "\t"+"	System.out.println(\"Error: \"+Error+\" \t|\t Token encontrado: \"+LAToken.name+\" con el valor: \"+LAToken.value);"+"\n"+
            "\t"+"}"+"\n"+
         "");
         this.metodos_parser.add(SynError);
    }
}